#include <iostream>
#include <fstream>
#include <sstream>
#include <list>
#include <unordered_map>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/http/chunk_encode.hpp>
#include <boost/asio.hpp>
#include <boost/mysql.hpp>
#include <rapidjson/document.h>
#include "HttpConnection.h"
#include "AiClient.h"

enum HttpConnErr
{
	HttpConnErr_None,
	HttpConnErr_LoginJsonInvalid,
	HttpConnErr_LoginNameNone,
	HttpConnErr_LoginPasswordNone,
	HttpConnErr_LoginPasswordInvalid,
	HttpConnErr_LoginSqlPasswordInvalid,
	HttpConnErr_ChatListJsonInvalid,
	HttpConnErr_ChatListSqlTableCheck,
	HttpConnErr_ChatCreateJsonInvalid,
	HttpConnErr_ChatCreateUserInvalid,
	HttpConnErr_ChatCreateUserCheck,
	HttpConnErr_ChatCreateUserNoFind,
	HttpConnErr_ChatCreateUserChatCheck,
	HttpConnErr_ChatCreateUserChatId,
};

static std::unordered_map<std::string, std::string> contentType = 
	{
		{ "html", "text/html" },
		{ "css", "text/css" },
		{ "js", "application/javascript" },
		{ "png", "image/png" },
	};

static std::unordered_map<std::string, void(HttpConnection::*)()> postDo =
	{
		{ "/ai/login", &HttpConnection::doPostLogin },
		{ "/ai/chat", &HttpConnection::doPostChat },
		{ "/ai/chat-list", &HttpConnection::doPostChatList },
		{ "/ai/chat-create", &HttpConnection::doPostChatCreate }
	};

HttpConnection::HttpConnection(::boost::asio::io_context& ioc, ::boost::asio::ip::tcp::socket socket) : _ioc(ioc), _stream(std::move(socket)), _timer(_stream.get_executor())
{

}

void HttpConnection::start()
{
	readRequest();
}

void HttpConnection::doPost()
{
	auto doIt = postDo.find(_parser.get().target());
	if (doIt != postDo.end())
		(this->*doIt->second)();
	else
	{
		_res.result(::boost::beast::http::status::not_found);
		_res.set(::boost::beast::http::field::content_type, "text/plain");
		_res.body() = "target not found:";
		_res.body() += doIt->first;
		_res.prepare_payload();
		sendResponse();
	}
}

void HttpConnection::doPostLogin()
{
	std::string jsonString = ::boost::beast::buffers_to_string(_parser.get().body().data());
	rapidjson::StringStream jsonStream(jsonString.c_str());
	rapidjson::Document doc;
	doc.ParseStream(jsonStream);
	if (!doc.IsObject())
	{
		std::ostringstream os;
		os << "不是有效的Json:" << jsonString;
		return sendResponse(HttpConnErr_LoginJsonInvalid, os.str().c_str());
	}

	std::string name = doc["name"].GetString();
	if (name.empty())
	{
		std::ostringstream os;
		os << "name为空:" << jsonString;
		return sendResponse(HttpConnErr_LoginNameNone, os.str().c_str());
	}

	std::string password = doc["password"].GetString();
	if (password.empty())
	{
		std::ostringstream os;
		os << "password为空:" << jsonString;
		return sendResponse(HttpConnErr_LoginPasswordNone, os.str().c_str());
	}

	if (password.size() != 64)
	{
		std::ostringstream os;
		os << "password格式错误:" << jsonString;
		return sendResponse(HttpConnErr_LoginPasswordInvalid, os.str().c_str());
	}

	::boost::mysql::any_connection sqlConn(_ioc);
	::boost::mysql::connect_params params;
	params.server_address.emplace_host_and_port("127.0.0.1", 3306);
	params.username = "haifeng";
	params.password = "";
	params.database = "AI_CHAT";
	sqlConn.connect(params);

	::boost::mysql::statement sqlStmt = sqlConn.prepare_statement("SELECT PASSWORD, ID, CHAT FROM USERS WHERE NAME = ?");
	::boost::mysql::results sqlResult;
	sqlConn.execute(sqlStmt.bind(name), sqlResult);
	auto sqlRows = sqlResult.rows();
	if (sqlRows.empty())
		return sendResponse(0, nullptr);

	auto sqlRow = sqlRows.at(0);
	auto sqlPassword = sqlRow.at(0).as_blob();
	if (sqlPassword.size() != 32)
	{
		std::ostringstream os;
		os << "sqlPassword格式错误:" << jsonString;
		return sendResponse(HttpConnErr_LoginSqlPasswordInvalid, os.str().c_str());
	}

	bool passwordErr = false;
	for (int i = 0; i < 32; ++i)
	{
		if ((hex2ch(password[2 * i]) << 4 | hex2ch(password[2 * i + 1])) == sqlPassword[i])
			continue;

		passwordErr = true;
		break;
	}

	if (passwordErr)
		return sendResponse(0, nullptr);

	std::ostringstream resBody;
	resBody << "{\"ec\":0,\"user\":{\"id\":" << sqlRow.at(1).as_int64() << ",\"chat\":" << sqlRow.at(2).as_int64() << "}}";
	_res.result(::boost::beast::http::status::ok);
	_res.set(::boost::beast::http::field::content_type, "application/json");
	_res.body() = resBody.str();
	_res.prepare_payload();
	sendResponse();
}

void HttpConnection::doPostChat()
{
	std::cout << "doPostChat" << std::endl;
}

void HttpConnection::doPostChatList()
{
	std::string jsonString = ::boost::beast::buffers_to_string(_parser.get().body().data());
	rapidjson::StringStream jsonStream(jsonString.c_str());
	rapidjson::Document doc;
	doc.ParseStream(jsonStream);
	if (!doc.IsObject())
	{
		std::ostringstream os;
		os << "不是有效的Json:" << jsonString;
		return sendResponse(HttpConnErr_ChatListJsonInvalid, os.str().c_str());
	}
	                                  
	std::string sqlTable = "CHAT_";
	sqlTable += std::to_string(doc["id"].GetInt());
	::boost::mysql::any_connection sqlConn(_ioc);
	::boost::mysql::connect_params params;
	params.server_address.emplace_host_and_port("127.0.0.1", 3306);
	params.username = "haifeng";
	params.password = "";
	params.database = "AI_CHAT";
	sqlConn.connect(params);
	
	{
		::boost::mysql::statement sqlStmt = sqlConn.prepare_statement("SELECT COUNT(*) AS table_exists FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?;");
		::boost::mysql::results sqlResult;
		sqlConn.execute(sqlStmt.bind(sqlTable), sqlResult);
		auto sqlRows = sqlResult.rows();
		if (sqlRows.empty())
		{
			std::ostringstream os;
			os << "sql检测" << sqlTable << "失败:" << jsonString;
			return sendResponse(HttpConnErr_ChatListSqlTableCheck, os.str().c_str());
		}
		
		auto sqlRow = sqlRows.at(0);
		auto sqlCount = sqlRow.at(0).as_int64();
		if (!sqlCount)
		{
			_res.result(::boost::beast::http::status::ok);                                                   
			_res.set(::boost::beast::http::field::content_type, "application/json");                         
			_res.body() = "{\"ec\":0,\"list\":[]}";
			_res.prepare_payload();                                                                          
			sendResponse();
			return;
		}
	}

	std::string sqlQuery = "SELECT ID, TITLE FROM " + sqlTable;
	::boost::mysql::results sqlResult;
	sqlConn.execute(sqlQuery.c_str(), sqlResult);
	std::ostringstream osBody;
	osBody << "{\"ec\":0,\"list\":[";
	bool first = true;
	for (auto row : sqlResult.rows())
	{
		if (first)
			first = false;
		else
			osBody << ",";

		osBody << "{\"id\":" << row.at(0).as_int64() << ",\"title\":\"" << row.at(1).as_string() << "\"}";
	}

	osBody << "]}";
	_res.result(::boost::beast::http::status::ok);                                                   
	_res.set(::boost::beast::http::field::content_type, "application/json");                         
	_res.body() = osBody.str();                                                                     
	_res.prepare_payload();                                                                          
	sendResponse();

}

void HttpConnection::doPostChatCreate()
{
	std::string jsonString = ::boost::beast::buffers_to_string(_parser.get().body().data());
	rapidjson::StringStream jsonStream(jsonString.c_str());
	rapidjson::Document doc;
	doc.ParseStream(jsonStream);
	if (!doc.IsObject())
	{
		std::ostringstream os;
		os << "不是有效的Json:" << jsonString;
		return sendResponse(HttpConnErr_ChatCreateJsonInvalid, os.str().c_str());
	}

	auto usrId = doc["id"].GetInt();
	if (usrId < 1)
	{
		std::ostringstream os;
		os << "用户ID非法:" << jsonString;
		return sendResponse(HttpConnErr_ChatCreateUserInvalid, os.str().c_str());
	}

	::boost::mysql::any_connection sqlConn(_ioc);                                                    
	::boost::mysql::connect_params params; 
	params.server_address.emplace_host_and_port("127.0.0.1", 3306);
	params.username = "haifeng";
	params.password = "";
	params.database = "AI_CHAT"; 
	sqlConn.connect(params); 
	
	std::ostringstream sqlExist;
	sqlExist << "SELECT EXISTS(SELECT 1 FROM USERS WHERE ID=" << usrId << ") AS id_exists;";
	::boost::mysql::results sqlExistResults;
	sqlConn.execute(sqlExist.str().c_str(), sqlExistResults);
	if (sqlExistResults.rows().empty())
	{
		std::ostringstream os;
		os << "sql检测USERS失败:" << jsonString;
		return sendResponse(HttpConnErr_ChatCreateUserCheck, os.str().c_str());
	}

	auto sqlExistCount = sqlExistResults.rows().at(0).at(0).as_int64();
	if (!sqlExistCount)
	{
		std::ostringstream os;
		os << "未找到用户:" << jsonString;
		return sendResponse(HttpConnErr_ChatCreateUserNoFind, os.str().c_str());
	}

	std::string sqlTable = "CHAT_";
	sqlTable += std::to_string(usrId);
	std::ostringstream sqlCheckTable;
	sqlCheckTable << "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='" << sqlTable << "';";
	::boost::mysql::results sqlCheckTableResults;
	sqlConn.execute(sqlCheckTable.str().c_str(), sqlCheckTableResults);
	if (sqlCheckTableResults.rows().empty())
	{
		std::ostringstream os;
		os << "sql检测" << sqlTable << "失败:" << jsonString;
		return sendResponse(HttpConnErr_ChatCreateUserChatCheck, os.str().c_str());
	}
	
	auto sqlCheckTableCount = sqlCheckTableResults.rows().at(0).at(0).as_int64();
	if (!sqlCheckTableCount)
	{
		std::ostringstream sqlCreateTable;
		sqlCreateTable << "CREATE TABLE " << sqlTable << "(ID INT AUTO_INCREMENT PRIMARY KEY, TITLE VARCHAR(70) DEFAULT '新对话', CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP);";
		::boost::mysql::results sqlCreateResults;
		sqlConn.execute(sqlCreateTable.str().c_str(), sqlCreateResults);
	}

	std::ostringstream sqlInsert;
	sqlInsert << "INSERT INTO " << sqlTable << " () VALUES ();";
	::boost::mysql::results sqlInsertResults;
	sqlConn.execute(sqlInsert.str().c_str(), sqlInsertResults);
	::boost::mysql::results sqlIdResults;
	sqlConn.execute("SELECT LAST_INSERT_ID();", sqlIdResults);
	if (sqlIdResults.rows().empty())
	{
		std::ostringstream os;
		os << "sql获取id失败:" << jsonString;
		return sendResponse(HttpConnErr_ChatCreateUserChatId, os.str().c_str());
	}

	std::ostringstream resBody;
	resBody << "{\"ec\":0,\"chat\":" << sqlIdResults.rows().at(0).at(0).as_uint64() << "}";
	_res.result(::boost::beast::http::status::ok);
	_res.set(::boost::beast::http::field::content_type, "application/json");
	_res.body() = resBody.str();
	_res.prepare_payload();
	sendResponse();
}

void HttpConnection::readRequest()
{
	auto pSelf = shared_from_this();
    ::boost::beast::http::async_read(
		_stream,
		_buffer,
		_parser,
		[pSelf](::boost::beast::error_code ec, size_t)
			{
				if (ec)
					pSelf->handleError(ec, "Read");
				else
					pSelf->processRequest();
			});
}

void HttpConnection::processRequest()
{
	auto& req = _parser.get();
	_res.set(::boost::beast::http::field::access_control_allow_origin, "*");
	_res.set(::boost::beast::http::field::access_control_allow_methods, "GET, POST, OPTIONS");
	_res.set(::boost::beast::http::field::access_control_allow_headers, "*");
	_res.version(req.version());

#if 0
	std::cout << "客户端: 本地: ";
	auto localEp = _socket.local_endpoint();
	auto localAddr = localEp.address();
	if (localAddr.is_v4())
		std::cout << localAddr.to_string() << ":" << localEp.port();
	else
		std::cout << "[" << localAddr.to_string() << "]:" << localEp.port();

	std::cout << ", 远程: ";
	auto remoteEp = _socket.remote_endpoint();
	auto remoteAddr = remoteEp.address();
	if (remoteAddr.is_v4())
	        std::cout << remoteAddr.to_string() << ":" << remoteEp.port();
	else
	        std::cout << "[" << remoteAddr.to_string() << "]:" << remoteEp.port();

	std::cout << ", 请求: " << req.method_string() << " - " << req.target() << " - " << req.version() << '\n';
	std::cout << "\thead:" << '\n';
	for (auto& field : req)
		std::cout << "\t\t" << field.name_string() << ": " << field.value() << '\n';

	std::cout << "\tbody:" << '\n';
	std::cout << "\t\t" << ::boost::beast::buffers_to_string(req.body().data()) << '\n';
	std::cout << std::string(50, '-') << std::endl;
#endif

	switch (req.method())
	{	
	case ::boost::beast::http::verb::get:
		{
			std::string path = "./Site";
			std::string type;
			auto target = req.target();
			auto posDot = target.find_last_of('.');
			if (std::string_view::npos == posDot)
			{
				path += "/html/index.html";
				type = "html";
			}
			else
			{
				path += target.substr(3);
				type = target.substr(posDot + 1);
			}

			auto contentTypeIt = contentType.find(type);
			if (contentTypeIt == contentType.end())
			{
				_res.result(::boost::beast::http::status::not_found);
				_res.set(::boost::beast::http::field::content_type, "text/plain");
				_res.body() = "type not found";
				_res.prepare_payload();
				sendResponse();
				break;
			}

			// 读取图片文件并返回
			::boost::beast::error_code ec;
			::boost::beast::file_stdio file;
			file.open(path.c_str(), ::boost::beast::file_mode::read, ec); // 确保图片文件存在
			if (ec)
			{
			    _res.result(::boost::beast::http::status::not_found);
				_res.set(::boost::beast::http::field::content_type, "text/plain");
			    _res.body() = "file not found";
				_res.prepare_payload();
				sendResponse();
				break;
			}

			auto fileSize = file.size(ec);
			if (ec)
			{
				_res.result(::boost::beast::http::status::internal_server_error);
				_res.set(::boost::beast::http::field::content_type, "text/plain");
				_res.body() = "failed to read file size";
				_res.prepare_payload();
				sendResponse();
				break;
			}

			std::string fileContent;
			fileContent.resize(fileSize);
			file.read(fileContent.data(), fileSize, ec);

			_res.result(::boost::beast::http::status::ok);
			_res.set(::boost::beast::http::field::content_type, contentTypeIt->second);
			_res.body() = std::move(fileContent);
			_res.prepare_payload();
			sendResponse();
		}
		break;
	case ::boost::beast::http::verb::options:
		_res.result(::boost::beast::http::status::no_content);
		sendResponse();
		break;
	case ::boost::beast::http::verb::post:
		{
			doPost();
			/*
			auto pSelf = shared_from_this();
			auto pAiClient = std::make_shared<AiClient>(_ioc, "127.0.0.1", "11435");
			auto& aiClient = *pAiClient;

           	 // 构造响应消息
	       	_res.result(::boost::beast::http::status::ok);
			_res.set(::boost::beast::http::field::content_type, "text/plain");
			_res.chunked(true);
			_res.set(::boost::beast::http::field::transfer_encoding, "chunked");
			_res.keep_alive(true);
			_res.set(::boost::beast::http::field::connection, "keep-alive");

			aiClient.setAddDataCallback([&body = _chunkBody](std::string_view data)
				{
					if (body.empty())
						body.emplace_back();

					auto& str =  body.front();
					str += data;
				});

			aiClient.setSendDataCallback(::boost::beast::bind_front_handler(&HttpConnection::sendChunk, pSelf));
			aiClient.setFinishDataCallback([&finish = _chunkFinish]() { finish = true; });
			//pAiClient->post("/api/generate", "{\"model\":\"deepseek-r1:8b\",\"prompt\":\"::boost::beast::http::async_write分块发送\",\"stream\":true}");
			aiClient.post("/api/generate", "{\"model\":\"deepseek-r1:8b\",\"prompt\":\"如何学习强化学习\",\"stream\":true}");
			*/
		}

		break;
	default:
		{
		        // 返回方法不允许错误
			_res.result(::boost::beast::http::status::method_not_allowed);
			_res.set(::boost::beast::http::field::content_type, "text/plain");
			_res.body() = "Method not allowed";
			_res.prepare_payload();
			sendResponse();
		}

		break;
	}
}

void HttpConnection::sendResponse()
{
	// 异步发送响应
	::boost::beast::http::async_write(
	    _stream,
	    _res,
		[pSelf = shared_from_this()](::boost::beast::error_code ec, size_t)
			{
				pSelf->shutdown(ec);
			});
}

void HttpConnection::sendResponse(char ec, const char* what)
{
	 if (what)
	 {
		switch(ec)
		{
		case HttpConnErr_LoginJsonInvalid:
			std::cout << "LoginJsonInvalid";
			break;
		case HttpConnErr_LoginNameNone:
			std::cout << "LoginNameNone";
			break;
		case HttpConnErr_LoginPasswordNone:
			std::cout << "LoginPasswordNone";
			break;
		case HttpConnErr_LoginPasswordInvalid:
			std::cout << "LoginPasswordInvalid";
			break;
		case HttpConnErr_LoginSqlPasswordInvalid:
			std::cout << "LoginSqlPasswordInvalid";
			break;
		case HttpConnErr_ChatListJsonInvalid:
			std::cout << "ChatListJsonInvalid";
			break;
		case HttpConnErr_ChatListSqlTableCheck:
			std::cout << "ChatListSqlTableCheck";
			break;
		case HttpConnErr_ChatCreateJsonInvalid:
			std::cout << "ChatCreateJsonInvalid";
			break;
		case HttpConnErr_ChatCreateUserInvalid:
			std::cout << "ChatCreateUserInvalid";
			break;
		case HttpConnErr_ChatCreateUserNoFind:
			std::cout << "ChatCreateUserNoFind";
			break;
		default:
			std::cout << "None";
			break;
		}

	    std::cout << "(" << (int)ec << "), " << what << std::endl;
	 }

	_res.result(::boost::beast::http::status::ok);
	_res.set(::boost::beast::http::field::content_type, "application/json");
	std::ostringstream os;
	os << "{\"ec\":" << (int)ec << "}";
	_res.body() = os.str();
	_res.prepare_payload();
	sendResponse();
}

void HttpConnection::sendChunk()
{
	if (_close)
		return;

	if (_chunkSending)
		return;

	if (_chunkHeader)
	{
		if (_chunkFinish)
		{
			_chunkSending = true;
			::boost::asio::async_write(
				_stream,
				::boost::asio::buffer("0\r\n\r\n"),
				[pSelf = shared_from_this()] (::boost::beast::error_code ec, size_t)
					{
						auto& self = *pSelf;
						self._chunkSending = false;
						self.shutdown(ec);
						if (ec)
							self.handleError(ec, "sendChunkLast");
					});
		}
		else if (_chunkBody.size())
		{
			_chunkSending = true;
			std::ostringstream oss;
			oss << std::hex << _chunkBody.front().size() << "\r\n" << _chunkBody.front() << "\r\n";
			_chunkBody.pop_front();
			::boost::asio::async_write(
				_stream,
				::boost::asio::buffer(oss.str()), 
				[pSelf = shared_from_this()] (::boost::beast::error_code ec, size_t)
					{
						auto& self = *pSelf;
						self._chunkSending = false;
						if (ec)
						{
							self.shutdown(ec);
							self.handleError(ec, "sendChunkData");
						}
						else
							self.sendChunk();
					});
		}
	}
	else
	{
		_chunkHeader = true;
		_chunkSending = true;
		auto pSr = std::make_shared<::boost::beast::http::response_serializer<::boost::beast::http::string_body>>(_res);
		::boost::beast::http::async_write_header(_stream, *pSr, [pSelf = shared_from_this(), pSr](::boost::beast::error_code ec, size_t)
			{
				auto& self = *pSelf;
				self._chunkSending = false;
				if (ec)
				{
					self.handleError(ec, "sendChunkHeader");
					self.shutdown(ec);
				}
				else
					self.sendChunk();
			});
	}


}

void HttpConnection::shutdown(::boost::beast::error_code ec)
{
	if (_close)
		return;

	_stream.socket().shutdown(::boost::asio::ip::tcp::socket::shutdown_send, ec);
	_close = true;
}

void HttpConnection::handleError(::boost::beast::error_code ec, const char* what)
{

    if (ec == ::boost::beast::http::error::end_of_stream || ec == ::boost::asio::error::connection_reset)
		return; // 正常关闭连接

	std::cout << "连接报错： " << what << " - " << ec.message() << std::endl;
}

char HttpConnection::hex2ch(char hex)
{
	switch (hex)
	{
	case '1':
		return 1;
	case '2':
		return 2;
	case '3':
		return 3;
	case '4':
		return 4;
	case '5':
		return 5;
	case '6':
		return 6;
	case '7':
		return 7;
	case '8':
		return 8;
	case '9':
		return 9;
	case 'a':
	case 'A':
		return 10;
	case 'b':
	case 'B':
		return 11;
	case 'c':
	case 'C':
		return 12;
	case 'd':
	case 'D':
		return 13;
	case 'e':
	case 'E':
		return 14;
	case 'f':
	case 'F':
		return 15;
	default:
		break;
	}

	return 0;
}
